package redis

import (
	redisCluster "github.com/go-redis/redis/v8"
	"github.com/gomodule/redigo/redis"
	"time"
)

type MyRedis struct {
	conn   redis.Conn
	expire time.Duration
	ticker *time.Ticker
	key    string
	id     string
}

func Create(addr string) (c redis.Conn, err error) {
	c, err = redis.Dial("tcp", addr)
	return
}

//创建连接池
//@addr redis地址
//@maxActive 最大连接数，如果为0，那么就默认为10000
func CreatePool(addr string, maxActive int) (pool *redis.Pool, err error) {
	pool = &redis.Pool{ //实例化一个连接池
		MaxIdle:     16,        //最初的连接数量
		MaxActive:   maxActive, //连接池最大连接数量,不确定可以用0（0表示自动定义），按需分配
		IdleTimeout: 300,       //连接关闭时间 300秒 （300秒不使用自动关闭）
		Dial: func() (redis.Conn, error) { //要连接的redis数据库
			return redis.Dial("tcp", addr)
		},
		Wait: true,
	}
	if maxActive == 0 {
		pool.MaxActive = 10000
	}

	return
}

//创建redis cluster对象
func CreateCluster(nodes []string) *redisCluster.ClusterClient {
	cluster := redisCluster.NewClusterClient(&redisCluster.ClusterOptions{
		Addrs:        nodes,
		MinIdleConns: 1000,
		MaxConnAge:   5000,
	})

	return cluster
}

func (m *MyRedis) DistLockWatch(key string) {
	m.ticker = time.NewTicker(1 * time.Second)
	for {
		select {
		case <-m.ticker.C:
			m.conn.Do("PEXPIRE", key, m.expire/3)
		}
	}
}

func GetLock(conn redis.Conn,key string, id string, expire time.Duration) *MyRedis {
	return &MyRedis{
		conn:   conn,
		expire: expire,
		ticker: nil,
		key: key,
		id: id,
	}
}

func (m *MyRedis) Lock() (result bool, leftTime int, err error) {
	luaScript := //如果锁不存在，则通过hset设置它的值，并设置过期时间
		"if (redis.call('exists', KEYS[1]) == 0) then " +
			"redis.call('hset', KEYS[1], ARGV[1], 1); " +
			"redis.call('pexpire', KEYS[1], ARGV[2]); " +
			"return nil; " +
			"end; " +
			//如果锁已存在，并且锁的是当前线程，则通过hincrby给数值递增1
			"if (redis.call('hexists', KEYS[1], ARGV[1]) == 1) then " +
			"redis.call('hincrby', KEYS[1], ARGV[1], 1); " +
			"redis.call('pexpire', KEYS[1], ARGV[2]); " +
			"return nil; " +
			"end; " +
			//如果锁已存在，但并非本线程，则返回过期时间ttl
			"return redis.call('pttl', KEYS[1]);"
	r, err := m.conn.Do("eval", luaScript, 1, m.key, m.id, m.expire)
	if err != nil {
		return
	}
	m.DistLockWatch(m.key)
	if r == nil {
		result = true
		return
	}
	leftTime = int(r.(int64))
	return
}
func (m *MyRedis) UnLock() (result bool, err error) {
	luaScript := //如果锁已经不存在， 发布锁释放的消息
		"if (redis.call('exists', KEYS[1]) == 0) then " +
			//"redis.call('publish', KEYS[2], ARGV[1]); " +
			"return 1; " +
			"end;" +
			//如果释放锁的线程和已存在锁的线程不是同一个线程，返回null
			"if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then " +
			"return nil;" +
			"end; " +
			//通过hincrby递减1的方式，释放一次锁
			//若剩余次数大于0 ，则刷新过期时间
			"local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1); " +
			"if (counter > 0) then " +
			"redis.call('pexpire', KEYS[1], ARGV[2]); " +
			"return 0; " +
			//否则证明锁已经释放，删除key并发布锁释放的消息
			"else " +
			"redis.call('del', KEYS[1]); " +
			//"redis.call('publish', KEYS[2], ARGV[1]); " +
			"return 1; " +
			"end; " +
			"return nil;"
	r, err := m.conn.Do("eval", luaScript, 1, m.key, m.id, m.expire)
	if err != nil {
		return
	}
	if r == nil || int(r.(int64)) == 0 {
		result = false
		return
	}
	m.ticker.Stop()
	result = true
	return
}
